package com.hp.bon.sgw.threads;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.hp.bon.sgw.service.SGWDataStoreService;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.bon.sgw.util.Comutils;

/**
 * 守护SGWDataStore，完成数据的入库等操作
 * 
 * @author wuzhih
 * 
 */
public class SGWDataStoreWatchDog extends Thread {
	private SGWDataStoreService dsService = null;
	/**
	 * Logger for this class
	 */
	private static final Logger logger = LoggerFactory.getLogger(SGWDataStoreWatchDog.class);

	Map<String, FileWriter> fileStreamMap = new HashMap<String, FileWriter>();

	public SGWDataStoreWatchDog() {
		this.setName("SGWDataStoreWatchDog");
		this.setDaemon(true);
	}

	public void run() {
		while (!Thread.interrupted()) {
			try {
				Thread.sleep(1 * 1000);
				if (dsService != null) {
					doWork();
				} else {
					dsService = SpringContext.getInstance().getDataStoreService();
				}
			} catch (InterruptedException e) {
				break;
			} catch (Exception e) {
				e.printStackTrace();
				logger.warn("caught exception " + e);
			}
		}
		logger.info("shutdowned");
	}

	public void shutdown() {
		this.interrupt();
	}

	public void doWork() {
		// 如果有domain 对象需要入库，则执行
		if (dsService.hasDomainDataToSave()) {
			saveDomainDatas();
		}
		// 如果有update sql 则执行
		if (dsService.hasSqlToExecute()) {
			bulkExecSql();
		}
		// 如果有需要保存为json的数据则执行
		if (dsService.hasJsonToSave()) {
			saveJsonFile();
		}
	}

	private FileWriter getoutStream(String objClsName) {
		String fileName = objClsName + "_" + Comutils.datafileFormat() + ".json";
		FileWriter out = this.fileStreamMap.get(fileName);
		if (out == null) {
			try {
				File file = new File(dsService.getJsonFileDir());
				if (!file.exists()) {
					file.mkdirs();
				}
				out = new FileWriter(new File(file, fileName), true);
				fileStreamMap.put(fileName, out);
			} catch (IOException e) {
				logger.warn("create out file err:" + e);
			}
		}
		return out;
	}

	private void saveJsonFile() {
		ConcurrentLinkedQueue<Object> queue = dsService.getJsonObjQueue();
		ArrayList<Object> batchData = new ArrayList<Object>();
		Object data = null;
		try {
			while ((data = queue.poll()) != null) {
				batchData.add(data);
				if (batchData.size() == 2000) {
					saveJsonDataToDisk(batchData);
					batchData.clear();
				}
			}
			// 最后一批
			if (!batchData.isEmpty()) {
				saveJsonDataToDisk(batchData);
			}
		} finally {
			closeFiles();
		}

	}

	private void closeFiles() {
		// 关闭文件
		if (fileStreamMap != null) {
			for (FileWriter outStream : fileStreamMap.values()) {
				try {
					outStream.close();
				} catch (IOException e) {
					logger.warn("close data file err:" + e.toString());
				}
			}
			fileStreamMap.clear();
		}
	}

	@SuppressWarnings("rawtypes")
	private void saveDomainDatas() {
		ConcurrentLinkedQueue queue = dsService.getDataQueue();
		ArrayList<Object> batchData = new ArrayList<Object>(100);
		Object data = null;
		try {
			while ((data = queue.poll()) != null) {
				batchData.add(data);
				if (batchData.size() == 2000) {
					SpringContext.getInstance().getOuterDBDataService().bulkSaveToDB(batchData);
					batchData.clear();
				}
			}
			// 最后一批
			if (!batchData.isEmpty()) {
				SpringContext.getInstance().getOuterDBDataService().bulkSaveToDB(batchData);
				batchData.clear();
			}
		} finally {
			batchData = null;
		}
	}

	private void bulkExecSql() {
		ConcurrentLinkedQueue<String> updateSqlQueue = dsService.getUpdateSqlQueue();
		List<String> sqls = new LinkedList<String>();
		String sqlStr = null;
		int batchSize = 100;
		while ((sqlStr = updateSqlQueue.poll()) != null) {
			sqls.add(sqlStr);
			if (sqls.size() > batchSize) {
				break;
			}
		}
		if (!sqls.isEmpty()) {
			dsService.bulkExecSql(sqls);
		}
	}

	private void saveJsonDataToDisk(ArrayList<? extends Object> batchData) {
		long begin = System.currentTimeMillis();
		try {
			Gson gson = new GsonBuilder().setDateFormat("yyyyMMddHHmmss.SSS").create();
			for (Object data : batchData) {
				String clsName = data.getClass().getSimpleName();
				FileWriter outStream = this.getoutStream(clsName);
				if (outStream != null) {
					try {
						outStream.write(gson.toJson(data));
						outStream.write("\r\n");
					} catch (Exception e) {
						logger.warn("write data file err:" + e.toString());
					}
				}
				data = null;
			}
		} catch (Exception e) {
			logger.warn("cuaght err:" + e.toString());
		}
		long used = System.currentTimeMillis() - begin;

		if (used > 5 * 1000) {
			logger.warn("write file too slow ,record num=" + batchData.size() + ",used time ms=" + used);
		}
	}

	@Autowired
	public void setDsService(SGWDataStoreService dsService) {
		this.dsService = dsService;
	}

}
